/*
 * Copyright (c) 2010-2020 Belledonne Communications SARL.
 *
 * This file is part of linphone-android
 * (see https://www.linphone.org).
 *
 * This program is free software: you can redistribute it and/or modify
 * it under the terms of the GNU General Public License as published by
 * the Free Software Foundation, either version 3 of the License, or
 * (at your option) any later version.
 *
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU General Public License for more details.
 *
 * You should have received a copy of the GNU General Public License
 * along with this program. If not, see <http://www.gnu.org/licenses/>.
 */
package com.newlink.building.activities.call

import android.Manifest
import android.animation.Animator
import android.animation.AnimatorSet
import android.animation.ObjectAnimator
import android.annotation.TargetApi
import android.app.PictureInPictureParams
import android.app.ProgressDialog
import android.app.Service
import android.bluetooth.BluetoothAdapter
import android.bluetooth.BluetoothHeadset
import android.content.BroadcastReceiver
import android.content.Context
import android.content.Intent
import android.content.IntentFilter
import android.content.pm.ActivityInfo
import android.content.pm.PackageManager
import android.content.res.Configuration
import android.content.res.Resources
import android.media.AudioManager
import android.net.Uri
import android.os.Build
import android.os.Bundle
import android.os.Handler
import android.os.Looper
import android.os.SystemClock
import android.provider.Settings
import android.util.Rational
import android.view.KeyEvent
import android.view.LayoutInflater
import android.view.View
import android.view.ViewGroup
import android.view.WindowManager
import android.widget.ImageView
import android.widget.RelativeLayout
import android.widget.TextView
import androidx.annotation.RequiresApi
import androidx.appcompat.app.AlertDialog
import androidx.databinding.DataBindingUtil
import androidx.lifecycle.ViewModelProvider
import androidx.lifecycle.lifecycleScope
import androidx.recyclerview.widget.GridLayoutManager
import androidx.recyclerview.widget.RecyclerView
import androidx.window.layout.FoldingFeature
import com.alibaba.android.arouter.facade.annotation.Route
import com.alibaba.android.arouter.launcher.ARouter
import com.google.android.material.bottomsheet.BottomSheetDialog
import com.newlink.building.NL_App.Companion.coreContext
import com.newlink.building.NL_App.Companion.corePreferences
import com.newlink.building.R
import com.newlink.building.activities.call.adapter.Module_Phone_DtmfAdapter
import com.newlink.building.activities.call.viewmodels.CallViewModel
import com.newlink.building.activities.call.viewmodels.CallViewModelFactory
import com.newlink.building.activities.call.viewmodels.Module_Phone_CallsViewModel
import com.newlink.building.activities.call.viewmodels.Module_Phone_ControlsFadingViewModel
import com.newlink.building.adapter.Module_Phone_FuctionKeyAdapter
import com.newlink.building.bean.Module_Phone_FunctionKey
import com.newlink.building.bean.Module_Phone_VideoFunctionType
import com.newlink.building.common_base.base.BaseApplication.Companion.getDeviceDataByUsername
import com.newlink.building.common_base.constant.NL_Key
import com.newlink.building.common_base.constant.Base_RouterPath
import com.newlink.building.common_base.event.MessageEvent
import com.newlink.building.common_base.event.OpenDoorKcpActionEvent
import com.newlink.building.common_base.event.OpenDoorResultBean
import com.newlink.building.common_base.event.TYPE_APP_BLE_OPEN_DOOR_RESULT
import com.newlink.building.common_base.event.TYPE_APP_OPEN_DOOR_RESULT
import com.newlink.building.common_base.event.TYPE_APP_SHOW_WAITING_DIALOG
import com.newlink.building.common_base.event.TYPE_CALL_SEND_CREATE_CHANNEL_RESULT_INCALL
import com.newlink.building.common_base.event.TYPE_CALL_SEND_CREATE_CHANNEL_RESULT_INCOMING
import com.newlink.building.common_base.event.TYPE_CALL_SEND_CREATE_CHANNEL_RESULT_INCOMING_EARLYMEDIA
import com.newlink.building.common_base.event.TYPE_CALL_SEND_CREATE_CHANNEL_RESULT_TRYING
import com.newlink.building.common_base.event.TYPE_CALL_UPDATE_CHANNEL_RESULT
import com.newlink.building.common_base.event.TYPE_CALL_UPDATE_TOKEN_RESULT
import com.newlink.building.common_base.ext.showToast
import com.newlink.building.common_base.model.bean.DoorData
import com.newlink.building.common_base.utils.Base_DensityUtil
import com.newlink.building.common_base.utils.DialogUtil
import com.newlink.building.common_base.utils.NLog
import com.newlink.building.common_base.utils.MqttDoorOperator
import com.newlink.building.common_base.utils.RomUtils
import com.newlink.building.common_base.utils.ScreenUtil
import com.newlink.building.common_base.utils.StatusBarUtil
import com.newlink.building.common_base.utils.StatusBarUtil.DEFAULT_STATUS_BAR_ALPHA
import com.newlink.building.common_base.utils.StatusBarUtil.getStatusBarHeight
import com.newlink.building.common_base.widget.SpaceItemDecoration
import com.newlink.building.common_base.widget.ToastSettingDialog
import com.newlink.building.compatibility.Module_Phone_Compatibility
import com.newlink.building.databinding.CallActivityBinding
import com.newlink.building.library_rino.ConnectionController
import com.newlink.building.library_rino.listener.RinoEventListenerStub
import com.newlink.building.library_rino.utils.RinoAudioUtils
import com.newlink.building.manager.Module_Phone_VoipController
import com.newlink.building.utils.Module_Phone_AudioRouteUtils
import com.newlink.building.utils.Module_Phone_Event
import com.newlink.building.utils.Module_Phone_PermissionHelper
import com.newlink.building.views.Module_Phone_AudioRouteSelectDialog2
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.MainScope
import kotlinx.coroutines.delay
import kotlinx.coroutines.launch
import kotlinx.coroutines.withContext
import org.greenrobot.eventbus.EventBus
import org.greenrobot.eventbus.Subscribe
import org.greenrobot.eventbus.ThreadMode
import org.linphone.activities.call.receiver.HeadsetPlugReceiver
import org.linphone.activities.call.receiver.TYPE_BT_HEADSET
import org.linphone.activities.call.receiver.TYPE_COMMON_HEADSET
import org.linphone.core.AudioDevice
import org.linphone.core.Call
import org.linphone.core.Core
import org.linphone.core.CoreListenerStub
import org.linphone.core.tools.Log
import org.linphone.mediastream.Version
import java.util.Timer
import java.util.TimerTask
import kotlin.properties.Delegates


/**
 * @author wangyang
 * @date 2022/11/21
 * @description 视频通话页面
 */
@Route(path = Base_RouterPath.PATH_CALL)
class Module_Phone_CallActivity : Module_Phone_ProximitySensorActivity() {
    private lateinit var binding: CallActivityBinding
    private lateinit var viewModel: CallViewModel
    private lateinit var controlsFadingViewModel: Module_Phone_ControlsFadingViewModel
    private lateinit var callsViewModel: Module_Phone_CallsViewModel

    private var foldingFeature: FoldingFeature? = null

    private val manager: Module_Phone_VoipController = Module_Phone_VoipController.get()
    private var objectAnimator: ObjectAnimator? = null
    private var toYData by Delegates.notNull<Float>()
    private var duration: Long = 200L
    private var isMovedDown: Boolean = false
    private var isMute: Boolean = false
    private var isOnSeaker: Boolean = true
    private var isHolding: Boolean = false
    private lateinit var funcationKeyData: MutableList<Module_Phone_FunctionKey>
    private var timer: Timer? = null
    private val sHandler: Handler = Handler(Looper.getMainLooper())

    private var isMicEnable = true
    private var isSpeakerEnable = false
    private var joinChannelSuccess = false

    private var titlebarBack: ImageView? = null
    private var titlebarTitle: TextView? = null
    private var titlebarHangup: ImageView? = null
    private var fullscreenLayout: RelativeLayout? = null
    private var statusBarSpace: View? = null

    private var btStatusReceiver: HeadsetPlugReceiver? = null
    private var openDoorDialog: ToastSettingDialog? = null
    private var audioDeviceChangeReceiver: AudioDeviceChangeReceiver? = null


    private val rinoManager: ConnectionController by lazy {
        ConnectionController.getInstance(
            applicationContext
        )
    }
    private var audioManager: AudioManager? = null

    //记录held时间
    private var mRecorderHoldTime: Long = 0L

//    private var isOnMonitor:Boolean = false

    private val STREAM_DEVICES_CHANGED_ACTION = "android.media.STREAM_DEVICES_CHANGED_ACTION"

    init {
//        isMicEnable = manager.isMicEnabled()
//        isSpeakerEnable = AudioRouteUtils.isSpeakerAudioRouteCurrentlyUsed()
    }

    fun initData() {
        if (RomUtils.isHuawei()) {
            StatusBarUtil.setTranslucent(this, DEFAULT_STATUS_BAR_ALPHA)
        }

        funcationKeyData = getFunctionKeyData()
        manager.setAudioStreamMute(false)
        manager.setSpeakerVolume(findCurrentCall(), 1.0f)
//        toYData = DensityUtil.dp2px(this, 111).toFloat()
    }

    private fun getFunctionKeyData(): MutableList<Module_Phone_FunctionKey> {
        val list = mutableListOf<Module_Phone_FunctionKey>()
        val functionTypes = Module_Phone_VideoFunctionType.values()
//        if(isOnMonitor){
//            val functionTypes = MonitorFunctionType.values()
//            for (i in functionTypes.indices) {
//                var functionType = functionTypes[i]
//                list.add(FunctionKey(functionType.stringRes, functionType.drawableRes,functionType.code))
//            }

        if (isFullScreen) {
            val openDoorFunctionKey = Module_Phone_FunctionKey(
                R.string.function_opendoor,
                R.drawable.icon_opendoor,
                R.drawable.icon_opendoor_landscape,
                1
            )

            for (i in functionTypes.indices) {
                val functionType = functionTypes[i]
                if (functionType == Module_Phone_VideoFunctionType.MICROPHONE) {
                    list.add(
                        Module_Phone_FunctionKey(
                            functionType.stringRes,
                            if (isMute) R.drawable.icon_mic_mute else R.drawable.icon_mic_normal,
                            if (isMute) R.drawable.icon_mic_mute_landscape else R.drawable.icon_mic_normal_landscape,
                            functionType.code
                        )
                    )
                } else {
                    list.add(
                        Module_Phone_FunctionKey(
                            functionType.stringRes,
                            functionType.drawableRes,
                            functionType.drawableResLandscape,
                            functionType.code
                        )
                    )
                }
            }
            list.add(1, openDoorFunctionKey)

        } else {
            for (i in functionTypes.indices) {
                var functionType = functionTypes[i]
                if (functionType == Module_Phone_VideoFunctionType.MICROPHONE) {
//                    isMute = !isMicEnable
                    list.add(
                        Module_Phone_FunctionKey(
                            functionType.stringRes,
                            if (isMute) R.drawable.icon_mic_mute else R.drawable.icon_mic_normal,
                            if (isMute) R.drawable.icon_mic_mute_landscape else R.drawable.icon_mic_normal_landscape,
                            functionType.code
                        )
                    )
                } else {
                    list.add(
                        Module_Phone_FunctionKey(
                            functionType.stringRes,
                            functionType.drawableRes,
                            functionType.drawableResLandscape,
                            functionType.code
                        )
                    )
                }
            }
        }
        return list
    }

    private val functionKeyAdapter: Module_Phone_FuctionKeyAdapter by lazy {
        Module_Phone_FuctionKeyAdapter()
    }

    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        EventBus.getDefault().register(this)
        requestedOrientation = ActivityInfo.SCREEN_ORIENTATION_PORTRAIT
        initData()
        initMic()

        ScreenUtil.getScreenWidth(this@Module_Phone_CallActivity)
        ScreenUtil.getScreenHeight(this@Module_Phone_CallActivity)

        val remoteSipAddress = intent.getStringExtra("REMOTE_ADDRESS")
        Log.e("[CallActivity] *************  $remoteSipAddress")
        remoteSipAddress?.let {
            Log.e("[CallActivity] *************  2222222")
            val core: Core = coreContext.core
            val remoteAddress = core.interpretUrl(it)
            val call =
                if (remoteAddress != null) core.getCallByRemoteAddress2(remoteAddress) else null
            if (call == null) {
                Log.e("[Notification Broadcast Receiver] Couldn't find call from remote address $remoteSipAddress")
                return
            }
            coreContext.answerCall(call)
        }

        coreContext.inCallActivity = true
        Module_Phone_Compatibility.setShowWhenLocked(this, true)
        Module_Phone_Compatibility.setTurnScreenOn(this, true)

        binding = DataBindingUtil.setContentView(this, R.layout.call_activity)
        binding.lifecycleOwner = this
        val currentCall: Call? = findCurrentCall()
        if (currentCall == null) {
            Log.e("[Call Activity] Couldn't find call in state Incoming")
            if (isTaskRoot) {
                Log.i("[Call Activity] Task is root, starting MainActivity")
                // When resuming app from recent tasks make sure MainActivity will be launched if there is no call
                ARouter.getInstance().build(Base_RouterPath.PATH_HOME)
                    .withFlags(Intent.FLAG_ACTIVITY_NEW_TASK).navigation()
//                val intent = Intent()
//                intent.setClass(this, LinphoneMainActivity::class.java)
//                intent.flags = Intent.FLAG_ACTIVITY_NEW_TASK
//                startActivity(intent)
            }
            finish()
            return
        }



        viewModel = ViewModelProvider(
            this,
            CallViewModelFactory(currentCall)
        )[CallViewModel::class.java]
        binding.viewModel = viewModel
        //从视频预览接通不用重新获取token重新加入通道
        if (rinoManager.getAgoraUserToken() != null && rinoManager.hasJoinChanneJob() && corePreferences.acceptEarlyMedia) {
            Log.i("[Call Activity] jump from preview. need joinChannel")
            rinoManager.cleanJoinChanneJob()
            //正常来说只需要leaveChannel再joinChannel即可,因sdk有问题先释放IPC sdk
            joinChannelSuccess = true
            rinoManager.setMuteAudio(false)
            rinoManager.joinChannel(
                binding.remoteVideoSurface,
                agoraId,
                isSpeakerOn = viewModel.curCallIsVideoEnable.value == true
            )
            rinoManager.startPushAudioToChannel(agoraId)
        } else {
            createRinoChannel()
        }

        controlsFadingViewModel = ViewModelProvider(this)[Module_Phone_ControlsFadingViewModel::class.java]
        binding.controlsFadingViewModel = controlsFadingViewModel

        callsViewModel = ViewModelProvider(this)[Module_Phone_CallsViewModel::class.java]

        statusBarSpace = findViewById<View>(R.id.status_bar_space)
        statusBarSpace?.postDelayed({
            NLog.e("===========update status bar height============")
            updateStatusbar()
            statusBarSpace?.requestLayout()
        }, 600)




        viewModel.callReleasedEvent.observe(
            this
        ) {
            it.consume {
                Log.i("[Call Activity] Call released, finish activity")
//                endPip()
                if (coreContext.core.callsNb == 0) {
                    hideLoading()
                    finish()
                }
            }
        }

        viewModel.callConnectedEvent.observe(
            this
        ) {
            it.consume {
                Log.i("[Call Activity] Call connected, finish activity")
//                endPip()
                finish()
            }
        }

        binding.localPreviewVideoSurface.setOnClickListener {
            if (viewModel.curNativePreviewWindowIdIsLocal.value != null) {
                viewModel.curNativePreviewWindowIdIsLocal.value =
                    !viewModel.curNativePreviewWindowIdIsLocal.value!!
            }
        }

        viewModel.curNativePreviewWindowIdIsLocal.observe(this) {
            // 大小画面切换

        }

        // 遍历所有可用的相机，并选择前摄像头
        for (camera in coreContext.core.videoDevicesList) {
            if (camera.contains("Front")) {
                coreContext.core.videoDevice = camera
                break
            }
        }

        callsViewModel.callUpdateEvent.observe(
            this
        ) {
            it.consume { call ->
                if (call.state == Call.State.StreamsRunning) {
                } else if (call.state == Call.State.UpdatedByRemote) {
                    if (coreContext.core.isVideoCaptureEnabled || coreContext.core.isVideoDisplayEnabled) {
                        if (call.currentParams.isVideoEnabled != call.remoteParams?.isVideoEnabled) {
                            callsViewModel.answerCallVideoUpdateRequest(call, true)
                        }
                    }
                }
            }
        }

        if (Version.sdkAboveOrEqual(Version.API23_MARSHMALLOW_60)) {
            checkPermissions()
        }

        val remoteSipAccount = manager.getRemoteSipAccount()
        val displayName =
            getDeviceDataByUsername(remoteSipAccount)?.devName ?: manager.getRemoteUsername()
//        binding.tvTitle.text = displayName
//        binding.tvTitle.text = currentCall.params.sessionName

        titlebarBack = findViewById(R.id.iv_titlebar_back)
        titlebarTitle = findViewById(R.id.tv_titlebar_title)
        titlebarHangup = findViewById(R.id.ib_fullscreen)
        fullscreenLayout = findViewById(R.id.call_fullscreen_layout)
        titlebarTitle?.text = displayName
//        if(isOnMonitor){
//            muteDevice(true)
//            titlebarBack?.visibility = View.GONE
//        }
        functionKeyAdapter.run {
            addChildClickViewIds()
            setOnItemChildClickListener { adapter, _, position ->
                val data = funcationKeyData[position]

                when (data.code) {
                    0 -> {
                        takeScreenshot(displayName)
                    }

                    1 -> {
                        val currentDevice =
                            getDeviceDataByUsername(remoteSipAccount)
                        currentDevice?.doors?.let {
                            if (it.size == 1) {
                                var doorNum = currentDevice.doors?.get(0)?.doorNo ?: 1
                                openDoor(currentDevice.mac, doorNum)
                            } else {
                                showBottomSheetDialog(currentDevice.mac, it)
                            }
                        }
                    }

                    2 -> {
                        rinoManager.stopPushAudioToChannel(agoraId)
                        safeHangup()
                    }

                    3 -> {
                        toggleMicrophone(data)
                        notifyDataSetChanged()
                    }

                    4 -> {
                        ARouter.getInstance().build(Base_RouterPath.PATH_HOME)
                            .withInt(NL_Key.TAB_INDEX, 2).navigation()
                        //                        manager.toggleRecording(!isRecording)
                        //                        isRecording = !isRecording
                        //                        type.stringRes =
                        //                            if (isRecording) R.string.video_is_recording else R.string.function_video_recording

                    }
                    //                    4 -> {
                    //                        muteDevice(false)
                    //                        isOnMonitor = false
                    //                        viewModel.curCallIsVideoEnable.value = true
                    //
                    //                        binding.rlFunctionkeysVideo.layoutManager  = GridLayoutManager(context, 4)
                    //                        functionKeyAdapter.setNewData(getFunctionKeyData())
                    //                        functionKeyAdapter.notifyDataSetChanged()
                    //                    }
                    else ->
                        showToast(resources.getString(funcationKeyData[position].stringRes))
                }
                showMomentarily()
                adapter.notifyDataSetChanged()
            }
            setNewData(funcationKeyData)
        }

        binding.rlFunctionkeysVideo.run {
            adapter = functionKeyAdapter
//            val spanCount = if(isOnMonitor) 3 else 4
//            val spanCount = VideoFunctionType.values().size
            val spanCount = if (isFullScreen) 4 else 3
            layoutManager = GridLayoutManager(context, spanCount)
        }

        binding.ibSpeakerVideo.setOnClickListener {
            val currentAudioDevice = currentCall?.outputAudioDevice
            Log.e("[fvl_dbg].... ${currentAudioDevice?.type}")

            viewModel.showRouteDialog.value = true
            showAudioRouteSelectPopup()
//            isOnSeaker = !isOnSeaker
//            manager.routeAudioToSpeaker(isOnSeaker)
//            var drawableRes = if (isOnSeaker) R.mipmap.ic_speaker else R.mipmap.ic_speaker_close
//            binding.ibSpeakerVideo.setImageDrawable(getDrawable(drawableRes))
        }

//        moveDown()
//        isMovedDown = true

//        titlebarHangup?.setOnClickListener(onClickListener)
        fullscreenLayout?.setOnClickListener(onClickListener)
        binding.ivFold.setOnClickListener(onClickListener)
        binding.remoteVideoSurface.setOnClickListener(onClickListener)
        rinoManager.addRinoEventListener(rinoEventListener)

//        binding.remoteVideoSurface.isOpaque = false
        binding.activeCallTimer.base =
            SystemClock.elapsedRealtime() - (1000 * viewModel.call.duration)
        binding.activeCallTimer.start()
//        startTimer()
        initSpeaker()


        binding.micphone.isSelected = !isMicEnable
//        binding.speaker.isSelected = !isSpeakerEnable
        binding.accountName.text = displayName

        binding.bottomControllerOpenDoor.setOnClickListener(onClickListener)
        binding.hangUp.setOnClickListener(onClickListener)
        binding.llMicphone.setOnClickListener(onClickListener)
        binding.llSpeaker.setOnClickListener(onClickListener)
        titlebarBack?.setOnClickListener(onClickListener)

        binding.activeVideocallTimer.base =
            SystemClock.elapsedRealtime() - (1000 * viewModel.call.duration)
        binding.activeVideocallTimer.start()

        viewModel.audioRouteChanged.observe(
            this
        ) {
            it.consume {
                showToast(getString(R.string.audio_route_changing))
            }
        }

        viewModel.curDeviceType.observe(
            this
        ) {
            lifecycleScope.launch {
                //从蓝牙切到听筒不会触发AudioDeviceChangeReceiver,所以延迟更新图标
                delay(200)
                withContext(Dispatchers.Main) {
                    updateAudioRouteIcon()
                }
            }
        }


        if (Version.sdkAboveOrEqual(Version.API23_MARSHMALLOW_60)) {
            checkPermissions()
        }

        btStatusReceiver = HeadsetPlugReceiver(object : HeadsetPlugReceiver.HeadsetPlugListener {
            override fun onHeadsetPlug(isPlug: Boolean, type: Int) {
                Log.e("[fvl_dbg] ..  耳机状态变化 :$isPlug ,耳机类型:$type ,  当前使用的语音通道 : ${viewModel.getCurrentAudioRoute()}")
                if (isPlug) {
                    if (viewModel.getCurrentAudioRoute() != AudioDevice.Type.Bluetooth && type == TYPE_BT_HEADSET) {
                        Log.e("[fvl_dbg] ..  尝试切换到蓝牙")
                        Handler(Looper.getMainLooper()).postDelayed(
                            {
                                if (Module_Phone_AudioRouteUtils.isBluetoothAudioRouteAvailable()) {
                                    Log.e("[fvl_dbg] .. 执行切换蓝牙")
                                    Module_Phone_AudioRouteUtils.routeAudioToBluetooth()
                                }
                            },
                            500
                        )
                    } else if (viewModel.getCurrentAudioRoute() != AudioDevice.Type.Headset && type == TYPE_COMMON_HEADSET) {
                        Log.e("[fvl_dbg] ..  尝试切换到耳机")
                        Handler(Looper.getMainLooper()).postDelayed(
                            {
                                if (Module_Phone_AudioRouteUtils.isHeadsetAudioRouteAvailable()) {
                                    Log.e("[fvl_dbg] ..  执行切换耳机")
                                    Module_Phone_AudioRouteUtils.routeAudioToHeadset()
                                }
                            },
                            500
                        )
                    }
                    viewModel.audioRouteChanged.value = Module_Phone_Event(true)
                } else {
                    if (viewModel.getCurrentAudioRoute() == AudioDevice.Type.Bluetooth && type == TYPE_BT_HEADSET) {
                        Log.e("[fvl_dbg] .. 蓝牙 切换回外放")
                        switchToDefaultAudioRoute()
                    } else if (viewModel.getCurrentAudioRoute() == AudioDevice.Type.Headset && type == TYPE_COMMON_HEADSET) {
                        Log.e("[fvl_dbg] .. 耳机 切换回外放")
                        switchToDefaultAudioRoute()
                    }
                }
            }
        })
        val intentFilter = IntentFilter().apply {
            addAction(BluetoothHeadset.ACTION_CONNECTION_STATE_CHANGED)
            addAction(Intent.ACTION_HEADSET_PLUG)
        }
        Log.e("[fvl_dbg] ..  注册耳机事件广播")
        registerReceiver(btStatusReceiver, intentFilter)
        initAudioRoute()
        registerAudioReceiver()
        coreContext.core.addListener(coreListener)
    }

    override fun initStatusBarColor() {
        super.initStatusBarColor(R.color.black)
    }

    private var isFullScreen = false
    private var lastClickTime = 0L

    override fun onConfigurationChanged(newConfig: Configuration) {
        super.onConfigurationChanged(newConfig)
        if (newConfig.orientation == Configuration.ORIENTATION_LANDSCAPE) {
            NLog.e("已切换到横屏 ${newConfig.fontScale}")
            updateControllerLayout(true)
        } else {
            NLog.e("已切换到竖屏 ${newConfig.fontScale}")

            updateControllerLayout(false)
        }

    }

    private fun toggleFullScreen() {
        val currentTimeMillis = System.currentTimeMillis()
        if (currentTimeMillis - lastClickTime < 800) {
            return
        }

        lastClickTime = currentTimeMillis
        if (isFullScreen) {
            NLog.e(">>>>>>>>>>>>> heng > shu")
            requestedOrientation = ActivityInfo.SCREEN_ORIENTATION_PORTRAIT
            titlebarHangup?.setImageResource(R.drawable.ic_enter_full_screen)
            isFullScreen = false

        } else {
            NLog.e(">>>>>>>>>>>>> shu > heng")
            requestedOrientation = ActivityInfo.SCREEN_ORIENTATION_LANDSCAPE
            titlebarHangup?.setImageResource(R.drawable.ic_exit_full_screen)
            isFullScreen = true

        }


    }

    private fun updateControllerLayout(isLandscape: Boolean) {

        updateStatusbar()

        if (isLandscape) {
            val landscapeHeight = ScreenUtil.getScreenUsableHeight(this@Module_Phone_CallActivity)
            val landscapeWidth = landscapeHeight * 16 / 9
            NLog.e(">>>>>>>>>>>>> shu > heng  isMute >> $isMute >> height = $landscapeHeight && width = $landscapeWidth")

            val params = RelativeLayout.LayoutParams(
                landscapeWidth,
                landscapeHeight
            ).apply {
                addRule(RelativeLayout.CENTER_HORIZONTAL)
                setMargins(0, 0, 0, 0)
            }

            binding.remoteVideoSurface.postDelayed({
                binding.remoteVideoSurface.layoutParams = params
                binding.remoteVideoSurface.requestLayout()
            }, 120)

        } else {
            val statusBarHeight = calAdapteStatusBarHeight()
            NLog.e(
                "[CallActivity] > updateControllerLayout ==== statusBarHeight : ${
                    ScreenUtil.getStatusBarHeight(
                        this@Module_Phone_CallActivity
                    )
                }"
            )
            NLog.e("[CallActivity] > updateControllerLayout ==== statusBarHeight : $statusBarHeight")

            val heightVal = if (RomUtils.isSamsung()) {
                statusBarHeight + 20
            } else {
                statusBarHeight
            }

            val height = Base_DensityUtil.dp2px(this@Module_Phone_CallActivity, 202) + heightVal
            val marginTop = Base_DensityUtil.dp2px(this@Module_Phone_CallActivity, 160) + statusBarHeight
            val params = RelativeLayout.LayoutParams(
                ViewGroup.LayoutParams.MATCH_PARENT, height
            ).apply {
                addRule(RelativeLayout.CENTER_HORIZONTAL)
                setMargins(0, marginTop, 0, 0)
            }

            binding.remoteVideoSurface.postDelayed({
                binding.remoteVideoSurface.layoutParams = params
                binding.remoteVideoSurface.requestLayout()
            }, 120)
        }

        binding.bottomControllerOpenDoorLayout.visibility =
            if (isFullScreen) View.GONE else View.VISIBLE

        funcationKeyData = getFunctionKeyData()
        NLog.e("[CallActivity] > updateControllerLayout ----> funcationKeyData : ${funcationKeyData.size}")
        functionKeyAdapter.apply {
            setNewData(funcationKeyData)
            updateScreenOrientation(isLandscape)
        }

        binding.rlFunctionkeysVideo.run {
            adapter = functionKeyAdapter
            val spanCount = if (isFullScreen) 4 else 3
            layoutManager = GridLayoutManager(context, spanCount)
        }

        val functionKeyHeight = if (isLandscape) {
            Base_DensityUtil.dp2px(this@Module_Phone_CallActivity, 60)
        } else {
            val statusBarHeight = calAdapteStatusBarHeight()
            if (RomUtils.isVivo()) {
                Base_DensityUtil.dp2px(this@Module_Phone_CallActivity, 140) + statusBarHeight
            } else {
                Base_DensityUtil.dp2px(this@Module_Phone_CallActivity, 140)
            }
        }

        NLog.e("[CallActivity] > updateController ----> height : $functionKeyHeight")
        val rlBottomBg =
            if (isLandscape) R.drawable.shape_functions_key_light_bg else R.drawable.shape_functions_key_bg

        binding.bottomControllerLayout.postDelayed({
            binding.bottomControllerLayout.layoutParams.width = ViewGroup.LayoutParams.MATCH_PARENT
            binding.bottomControllerLayout.layoutParams.height = functionKeyHeight
            binding.bottomControllerLayout.setBackgroundResource(rlBottomBg)
            binding.bottomControllerLayout.requestLayout()
        }, 200)

        val paramsPortal = RelativeLayout.LayoutParams(
            ViewGroup.LayoutParams.WRAP_CONTENT,
            ViewGroup.LayoutParams.WRAP_CONTENT
        ).apply {
            addRule(RelativeLayout.CENTER_HORIZONTAL)
            setMargins(0, Base_DensityUtil.dp2px(this@Module_Phone_CallActivity, 2), 0, 0)
        }

        val paramsLandscape = RelativeLayout.LayoutParams(
            ViewGroup.LayoutParams.WRAP_CONTENT,
            ViewGroup.LayoutParams.WRAP_CONTENT
        ).apply {
            addRule(RelativeLayout.CENTER_VERTICAL)
            setMargins(
                Base_DensityUtil.dp2px(this@Module_Phone_CallActivity, 30), 0, 0, 0
            )
        }

        binding.activeVideocallTimer.postDelayed({
            binding.activeVideocallTimer.layoutParams =
                if (isLandscape) paramsLandscape else paramsPortal
            binding.activeVideocallTimer.requestLayout()
        }, 200)

        showMomentarily()

    }

    private fun calAdapteStatusBarHeight() = if (RomUtils.isHuawei()
        || RomUtils.isOppo() || RomUtils.isVivo()
    ) {
        NLog.e("------status bar  is 0 -------")
        0
    } else if (RomUtils.isSamsung()) {
        20
    } else if (RomUtils.isXiaoMi()) {
        NLog.e("------status bar  is 1 -------")
        ScreenUtil.getStatusBarHeight(this@Module_Phone_CallActivity) - 50
    } else {
        ScreenUtil.getStatusBarHeight(this@Module_Phone_CallActivity)
    }

    private fun updateStatusbar() {
        if (RomUtils.isHuawei()) {
            val statusBarHeight = getStatusBarHeight(this@Module_Phone_CallActivity)
            statusBarSpace?.layoutParams?.height = statusBarHeight
        }
    }


    private fun animateViewSizeChange(
        view: View,
        targetWidth: Int,
        targetHeight: Int,
        duration: Long
    ) {
        val widthAnimator = ObjectAnimator.ofInt(view, "width", view.width, targetWidth)
        val heightAnimator = ObjectAnimator.ofInt(view, "height", view.height, targetHeight)

        widthAnimator.addUpdateListener { animation ->
            val layoutParams = view.layoutParams
            layoutParams.width = animation.animatedValue as Int
            view.layoutParams = layoutParams
        }

        heightAnimator.addUpdateListener { animation ->
            val layoutParams = view.layoutParams
            layoutParams.height = animation.animatedValue as Int
            view.layoutParams = layoutParams
        }

        val animatorSet = AnimatorSet()
        animatorSet.playTogether(widthAnimator, heightAnimator)
        animatorSet.duration = duration
        animatorSet.start()
    }

    private val coreListener = object : CoreListenerStub() {
        override fun onCallStateChanged(
            core: Core,
            call: Call,
            state: Call.State,
            message: String,
        ) {
            Log.e("[CallActivity] onCallStateChanged state:${state.name}")
            when (state) {
                Call.State.End, Call.State.Error, Call.State.Released -> {
                    //当收到二路来电的挂断事件,即处于通话状态的currentCall不为空,不销毁当前Activity
                    if (findCurrentCall() == null) {
                        hideLoading()
                        finish()
                    }
                }
                //暂时不需要Paused状态,CoreManager在失去音频焦点会调用pauseAllCalls,本地暂停通话或者对端暂停通话都要恢复一下
                Call.State.Paused -> {
                    Module_Phone_VoipController.get().resumeCall(call)
                }
                //对端held更新UI
                Call.State.PausedByRemote -> {
                    isHolding = true
                    binding.rlPause.visibility = View.VISIBLE
                    binding.accountIcon.setImageDrawable(getDrawable(R.drawable.call_held))
                    if (mRecorderHoldTime == 0L) {
                        mRecorderHoldTime = SystemClock.elapsedRealtime()
                    }
                    binding.activeVideocallTimer.stop()
                    binding.activeCallTimer.stop()
                }
                //恢复通话状态
                Call.State.StreamsRunning -> {
                    if (isHolding) {
                        isHolding = false
                        binding.rlPause.visibility = View.GONE
                        binding.accountIcon.setImageDrawable(getDrawable(R.drawable.head_in_call))
                        if (mRecorderHoldTime != 0L) {
                            binding.activeVideocallTimer.base += (SystemClock.elapsedRealtime() - mRecorderHoldTime)
                            binding.activeCallTimer.base += (SystemClock.elapsedRealtime() - mRecorderHoldTime)
                            mRecorderHoldTime = 0L
                        }
                        binding.activeVideocallTimer.start()
                        binding.activeCallTimer.start()
                    }
                }

                else -> {}
            }
        }
    }

    //注册声音通道切换,蓝牙切换的要单独添加action
    private fun registerAudioReceiver() {
        audioManager = getSystemService(Context.AUDIO_SERVICE) as AudioManager
        val intentFilter = IntentFilter()
        intentFilter.addAction(STREAM_DEVICES_CHANGED_ACTION)
        intentFilter.addAction(BluetoothAdapter.ACTION_CONNECTION_STATE_CHANGED)
        intentFilter.addAction(BluetoothAdapter.ACTION_STATE_CHANGED)
        audioDeviceChangeReceiver = AudioDeviceChangeReceiver();
        registerReceiver(audioDeviceChangeReceiver, intentFilter)
    }

    inner class AudioDeviceChangeReceiver : BroadcastReceiver() {
        override fun onReceive(context: Context, intent: Intent?) {

            val currentAudioRoute = viewModel.getCurrentAudioRoute()
            Log.e("[CallActivity] AudioDeviceChangeReceiver action:${intent?.action} currentAudioRoute:$currentAudioRoute")
            if (joinChannelSuccess) {
                //收到通道切换广播及时刷新UI
                updateAudioRouteIcon()
            }

        }
    }

    private fun takeScreenshot(deviceName: String = "") {
        val isSuccess =
            ConnectionController.getInstance(applicationContext)
                .takeSnapshot(this@Module_Phone_CallActivity, deviceName)
        Log.e("[jake]....截图结果:  $isSuccess")

    }

    private fun createRinoChannel() {
        rinoManager.setMuteAudio(false)
        rinoManager.setMicEnabled(true)
        Log.e("createRinoChannel ${rinoManager.getChannelName()}")
        if (rinoManager.getChannelName() != null) {
            rinoManager.joinChannel(
                binding.remoteVideoSurface,
                agoraId,
                isSpeakerOn = viewModel.curCallIsVideoEnable.value == true
            )
        }
//        val sipMsgEvent = MessageEvent(
//            TYPE_CALL_SEND_CREATE_CHANNEL_REQUEST_INCALL,
//            CallCreateActionBean(channelName = rinoManager.getRemoteChannelName(), aid = agoraId)
//        )
//        EventBus.getDefault().post(sipMsgEvent)
    }

    private val rinoEventListener = object : RinoEventListenerStub() {
        override fun onJoinChannelSuccess() {
            runOnUiThread {
                //根据设备的连接情况选择相应声道
//                if(viewModel.curCallIsVideoEnable.value == true) {
//                    RinoAudioUtils.changeToSpeaker(applicationContext)
//                } else {
//                    RinoAudioUtils.changeToEarpiece(applicationContext)
//                }
//                AudioUtils.choiceAudioModel(applicationContext, viewModel.curCallIsVideoEnable.value == true)
//                if (!isOnSeaker) {
//                    AudioUtils.changeToEarpiece(applicationContext)
//                }
                joinChannelSuccess = true
            }
        }

        override fun onLeaveChannel() {
            lifecycleScope.launch(Dispatchers.Main) {
                joinChannelSuccess = false
                val hangUp = manager.hangUp()
                //意外挂掉失败,结束当前Activity
                if (!hangUp) finish()
            }
        }

        override fun onSnapshotTaken(result: Int) {
            runOnUiThread {
                val result =
                    if (result == 0) getString(R.string.result_success) else getString(R.string.result_failure)
                showToast(getString(R.string.function_shortcut_result, result))
            }
        }

    }

    private fun initMic() {
        resetMicrophoneMute()
        rinoManager.setMicEnabled(isMicEnable)
    }

    private fun resetMicrophoneMute() {
        if (RinoAudioUtils.isMicrophoneMute(this)) {
            RinoAudioUtils.setMicrophoneMute(this, false)
        }
    }

    fun initAudioRoute() {
        //初始化时如果没有耳机连接默认打开扬声器,不在StreamsRunning状态时判断
        val currentCall = findCurrentCall() ?: return
        if (corePreferences.routeAudioToSpeakerWhenVideoIsEnabled && currentCall.currentParams?.isVideoEnabled == true) {
            // Do not turn speaker on when video is enabled if headset or bluetooth is used
            if (!Module_Phone_AudioRouteUtils.isHeadsetAudioRouteAvailable() && !Module_Phone_AudioRouteUtils.isBluetoothAudioRouteCurrentlyUsed(
                    currentCall
                )
            ) {
                Log.i("[CallActivity] Video enabled and no wired headset not bluetooth in use, routing audio to speaker")
                Module_Phone_AudioRouteUtils.routeAudioToSpeaker(currentCall)
            }
        }
    }

    private fun showAudioRouteSelectPopup() {
//        AudioRoutesSelectDialog(this@CallActivity, viewModel).apply {
//            show()
//        }
        Module_Phone_AudioRouteSelectDialog2(this@Module_Phone_CallActivity, viewModel, this@Module_Phone_CallActivity).apply {
            show()
        }
    }

    private fun updateAudioRouteIcon() {

        Log.e("[CallActivity] updateAudioRouteIcon currentAudioRoute:${viewModel.getCurrentAudioRoute()}")
//        binding.ivSpeaker.setImageResource(
//            when (viewModel.getCurrentAudioRoute()) {
//                AudioDevice.Type.Speaker -> R.drawable.audio_route_speaker
//                AudioDevice.Type.Bluetooth -> R.drawable.audio_route_bluetooth
//                AudioDevice.Type.Headset -> R.drawable.audio_route_headset
//                else -> R.drawable.audio_route_earpiece
//            }
//        )

        binding.ibSpeakerVideo.setBackgroundResource(
            when (viewModel.getCurrentAudioRoute()) {
                AudioDevice.Type.Speaker -> R.drawable.audio_route_speaker
                AudioDevice.Type.Bluetooth -> R.drawable.audio_route_bluetooth
                AudioDevice.Type.Headset -> R.drawable.audio_route_headset
                else -> R.drawable.audio_route_earpiece
            }
        )

        binding.speaker.setBackgroundResource(
            when (viewModel.getCurrentAudioRoute()) {
                AudioDevice.Type.Speaker -> R.drawable.audio_route_speaker
                AudioDevice.Type.Bluetooth -> R.drawable.audio_route_bluetooth
                AudioDevice.Type.Headset -> R.drawable.audio_route_headset
                else -> R.drawable.audio_route_earpiece
            }
        )

        binding.speakerTxt.setText(
            when (viewModel.getCurrentAudioRoute()) {
                AudioDevice.Type.Speaker -> R.string.route_type_speaker
                AudioDevice.Type.Bluetooth -> R.string.route_type_bt
                AudioDevice.Type.Headset -> R.string.route_type_headset
                else -> R.string.route_type_earpiece
            }
        )
    }

    private fun switchToDefaultAudioRoute() {
        viewModel.switchAudioRoute(AudioDevice.Type.Speaker)
//        binding.ivSpeaker.setImageResource(R.drawable.audio_route_speaker)
        binding.speaker.setBackgroundResource(R.drawable.audio_route_speaker)
        binding.ibSpeakerVideo.setBackgroundResource(R.drawable.audio_route_speaker)

        binding.speakerTxt.setText(
            R.string.route_type_speaker
        )
    }

    override fun onLayoutChanges(foldingFeature: FoldingFeature?) {
        this.foldingFeature = foldingFeature
        updateConstraintSetDependingOnFoldingState()
    }

    override fun onResume() {
        super.onResume()
        //由于Rino加入通道后自动切到扬声器,蓝牙连接时获取状态的速度慢,此处先循环获取当前通道,后续做优化
        if (RinoAudioUtils.isBluetoothA2dpOn(applicationContext)) {
            var count = 0
            lifecycleScope.launch(Dispatchers.IO) {
                do {
                    delay(500)
                    withContext(Dispatchers.Main) {
                        count++
                        Log.e("[CallActivity]  count:$count type:${viewModel.getCurrentAudioRoute()}")
                        if (viewModel.getCurrentAudioRoute() == AudioDevice.Type.Bluetooth) {
                            updateAudioRouteIcon()
                            count = 10
                        }
                    }
                } while (count < 10)
            }
        }

        //设置保持屏幕常亮
        window.addFlags(WindowManager.LayoutParams.FLAG_KEEP_SCREEN_ON)
        Log.e("[jake1] .. CallActivity..onResume")
        val currentCall = findCurrentCall()
        if (currentCall == null) {
            finish()
            return
        }
        if (corePreferences.fullScreenCallUI) {
            hideSystemUI()
            window.decorView.setOnSystemUiVisibilityChangeListener { visibility ->
                if (visibility and View.SYSTEM_UI_FLAG_FULLSCREEN == 0) {
                    lifecycleScope.launch {
                        delay(2000)
                        withContext(Dispatchers.Main) {
                            hideSystemUI()
                        }
                    }
                }
            }
        }

        var displayName = currentCall?.remoteAddress?.username
        if (displayName.isNullOrEmpty()) {
            displayName = currentCall?.remoteAddress?.domain
        }

        Handler().postDelayed(
            {
                // 解決Call Resume时，音/视频布局错乱
                viewModel.curCallIsVideoEnable.value =
                    currentCall?.currentParams?.isVideoEnabled
            },
            500
        )

        viewModel.curCallIsVideoEnable.value =
            currentCall?.currentParams?.isVideoEnabled


//        if (viewModel.curNativePreviewWindowIdIsLocal.value == true) {
//            coreContext.core.nativeVideoWindowId = binding.remoteVideoSurface
//            coreContext.core.nativePreviewWindowId = binding.localPreviewVideoSurface
//        } else {
//            coreContext.core.nativeVideoWindowId = binding.localPreviewVideoSurface
//            coreContext.core.nativePreviewWindowId = binding.remoteVideoSurface
//        }

        if (coreContext.core.callsNb == 0) {
            Log.w("[Call Activity] Resuming but no call found...")
            if (isTaskRoot) {
                // When resuming app from recent tasks make sure MainActivity will be launched if there is no call
                ARouter.getInstance().build(Base_RouterPath.PATH_HOME)
                    .withFlags(Intent.FLAG_ACTIVITY_NEW_TASK).navigation()
//                val intent = Intent()
//                intent.setClass(this, LinphoneMainActivity::class.java)
//                intent.flags = Intent.FLAG_ACTIVITY_NEW_TASK
//                startActivity(intent)
            }
            finish()
        } else {
            coreContext.removeCallOverlay()
        }
        lifecycleScope.launch {
            compatibilityForSpecialPhone()
        }
    }

    /**
     * 适配部分手机，视频通话时，通话时,切换到Speaker时,语音从听筒出的问题
     *
     */
    private suspend fun compatibilityForSpecialPhone() {
        delay(500)
        if (viewModel.curCallIsVideoEnable.value == true) {
            if (Module_Phone_Compatibility.isSamsungA5070()) {
                Log.e("[jake] .. Samsung A5070 .. switch to Earpiece")
                viewModel.switchAudioRoute(AudioDevice.Type.Earpiece)
                delay(200)
                Log.e("[jake] .. Samsung A5070 .. switch to speaker")
                viewModel.switchAudioRoute(AudioDevice.Type.Speaker)
            }
        }
    }

    override fun onPause() {
        //取消保持屏幕常亮
        window.clearFlags(WindowManager.LayoutParams.FLAG_KEEP_SCREEN_ON)
        val core = coreContext.core
        if (core.callsNb > 0) {
            coreContext.createCallOverlay()
        }
        coreContext.inCallActivity = false
        super.onPause()
    }

    @RequiresApi(Build.VERSION_CODES.N)
    override fun onDestroy() {
        NLog.e("call activity >> onDestroy")
        resetMicrophoneMute()
        if (btStatusReceiver != null) {
            unregisterReceiver(btStatusReceiver)
        }
        if (audioDeviceChangeReceiver != null) {
            unregisterReceiver(audioDeviceChangeReceiver)
        }
        stopTimer()
        binding.remoteVideoSurface.removeAllViews()
        rinoManager.removeRinoEventListener(rinoEventListener)
        coreContext.inCallActivity = false
        coreContext.core.nativeVideoWindowId = null
        coreContext.core.nativePreviewWindowId = null
        EventBus.getDefault().unregister(this)
        coreContext.core.removeListener(coreListener)
        super.onDestroy()
        releaseDialog()
    }


    override fun onBackPressed() {
        // block back button when calling
    }

    override fun onKeyDown(keyCode: Int, event: KeyEvent): Boolean {
        if (keyCode == KeyEvent.KEYCODE_BACK) {
            return true
        }
        // 拦截MENU按钮点击事件，让它无任何操作
        if (keyCode == KeyEvent.KEYCODE_MENU) {
            return true
        }

        return super.onKeyDown(keyCode, event)
    }

    override fun onUserLeaveHint() {
        super.onUserLeaveHint()

//        if (coreContext.isVideoCallOrConferenceActive()) {
//            Compatibility.enterPipMode(this)
//        }
    }


    @RequiresApi(Build.VERSION_CODES.O)
    override fun onPictureInPictureModeChanged(
        isInPictureInPictureMode: Boolean,
        newConfig: Configuration
    ) {
        super.onPictureInPictureModeChanged(isInPictureInPictureMode, newConfig)
        if (isInPictureInPictureMode) {
            controlsFadingViewModel.areControlsHidden.value = true
            binding.bottomControllerLayout.visibility = View.GONE
            binding.ibSpeakerVideo.visibility = View.GONE
//            binding.callTitleBar.ibHangupVideo.visibility = View.GONE
        } else {
            binding.bottomControllerLayout.visibility = View.VISIBLE
            binding.ibSpeakerVideo.visibility = View.VISIBLE
//            binding.callTitleBar.ibHangupVideo.visibility = View.VISIBLE
        }

        if (corePreferences.hideCameraPreviewInPipMode) {
            controlsFadingViewModel.isVideoPreviewHidden.value = isInPictureInPictureMode
        } else {
            controlsFadingViewModel.isVideoPreviewResizedForPip.value = isInPictureInPictureMode
        }
    }

    override fun getTheme(): Resources.Theme {
        val theme = super.getTheme()
        if (corePreferences.fullScreenCallUI) {
            theme.applyStyle(R.style.FullScreenTheme, true)
        }
        return theme
    }

    private fun hideSystemUI() {
        window.decorView.systemUiVisibility = View.SYSTEM_UI_FLAG_FULLSCREEN or
                View.SYSTEM_UI_FLAG_HIDE_NAVIGATION or
                View.SYSTEM_UI_FLAG_IMMERSIVE or
                View.SYSTEM_UI_FLAG_LAYOUT_FULLSCREEN or
                View.SYSTEM_UI_FLAG_LAYOUT_STABLE or
                View.SYSTEM_UI_FLAG_LAYOUT_HIDE_NAVIGATION
    }

    private fun updateConstraintSetDependingOnFoldingState() {

    }

    @TargetApi(Version.API23_MARSHMALLOW_60)
    private fun checkPermissions() {
        val permissionsRequiredList = arrayListOf<String>()

        if (!Module_Phone_PermissionHelper.get().hasRecordAudioPermission()) {
            Log.i("[Incoming Call Activity] Asking for RECORD_AUDIO permission")
            permissionsRequiredList.add(Manifest.permission.RECORD_AUDIO)
            permissionsRequiredList.add(Manifest.permission.FOREGROUND_SERVICE_MICROPHONE)
        }

//        if (!PermissionHelper.get().hasCameraPermission()
//        ) {
//            Log.i("[Incoming Call Activity] Asking for CAMERA permission")
//            permissionsRequiredList.add(Manifest.permission.CAMERA)
//        }

        if (permissionsRequiredList.isNotEmpty()) {
            val permissionsRequired = arrayOfNulls<String>(permissionsRequiredList.size)
            permissionsRequiredList.toArray(permissionsRequired)
            requestPermissions(permissionsRequired, 0)
        }
    }

    override fun onRequestPermissionsResult(
        requestCode: Int,
        permissions: Array<out String>,
        grantResults: IntArray
    ) {
        if (requestCode == 0) {
            for (i in permissions.indices) {
                when (permissions[i]) {
                    Manifest.permission.RECORD_AUDIO -> if (grantResults[i] == PackageManager.PERMISSION_GRANTED) {
                        Log.i("[Incoming Call Activity] RECORD_AUDIO permission has been granted")
                        coreContext.core.isMicEnabled = true
                        controlsFadingViewModel.isMicOpen.value = coreContext.core.isMicEnabled
                    } else {
                        showToast(getString(R.string.permission_content_audio))
                        viewModel.terminateCall()
                    }

                    Manifest.permission.CAMERA -> if (grantResults[i] == PackageManager.PERMISSION_GRANTED) {
                        Log.i("[Incoming Call Activity] CAMERA permission has been granted")
                        // 遍历所有可用的相机，并选择前摄像头
                        for (camera in coreContext.core.videoDevicesList) {
                            if (camera.contains("Front")) {
                                coreContext.core.videoDevice = camera
                                break
                            }
                        }
                        coreContext.core.reloadVideoDevices()
                    } else {
                        showToast(getString(R.string.permission_content_camera))
                        viewModel.terminateCall()
                    }

                    Module_Phone_Compatibility.BLUETOOTH_CONNECT -> if (grantResults[i] == PackageManager.PERMISSION_GRANTED) {
                        Log.i("[Incoming Call Activity] BLUETOOTH_CONNECT permission has been granted")
                    } else {
                        showToast(getString(R.string.permission_content_bluetooth))
                        viewModel.terminateCall()
                    }
                }
            }
        } else if (requestCode == 100) {
            if (grantResults[0] == PackageManager.PERMISSION_GRANTED) {
                Log.e("[jake] .. get permission of write external storage .... ")
                val remoteSipAccount = Module_Phone_VoipController.get().getRemoteSipAccount()
                val displayName =
                    getDeviceDataByUsername(remoteSipAccount)?.devName ?: Module_Phone_VoipController.get()
                        .getRemoteUsername()
                takeScreenshot(displayName)
            } else {
                showPermissionRationale()
            }
        }
        super.onRequestPermissionsResult(requestCode, permissions, grantResults)
    }

    private fun showPermissionRationale() {
        AlertDialog.Builder(this)
            .setTitle("权限请求")
            .setMessage("此功能需要访问存储权限，请到设置中手动打开权限。")
            .setPositiveButton("去设置") { _, _ ->
                openAppSettings()
            }
            .setNegativeButton("取消", null)
            .show()
    }

    private fun openAppSettings() {
        val intent = Intent(Settings.ACTION_APPLICATION_DETAILS_SETTINGS).apply {
            data = Uri.fromParts("package", packageName, null)
        }
        startActivity(intent)
    }

    private fun initSpeaker() {
        isOnSeaker = coreContext.core.currentCall?.params?.isVideoEnabled == true
        //视频通话自动切换到扬声器,语音电话切换到听筒
        rinoManager.setSpeakerEnabled(isOnSeaker)

        //根据当前设备的连接情况设置默认图标 理论上joinChannelSuccess后应切到此图标
        if (viewModel.isBluetoothAvailable()) {
//            binding.ivSpeaker.setImageResource(R.drawable.audio_route_bluetooth)
            binding.ibSpeakerVideo.setBackgroundResource(R.drawable.audio_route_bluetooth)
            binding.speaker.setBackgroundResource(R.drawable.audio_route_bluetooth)
            binding.speakerTxt.text = getString(R.string.route_type_bt)
        } else if (viewModel.isHeadsetAvailable()) {
//            binding.ivSpeaker.setImageResource(R.drawable.audio_route_headset)
            binding.ibSpeakerVideo.setBackgroundResource(R.drawable.audio_route_headset)
            binding.speaker.setBackgroundResource(R.drawable.audio_route_headset)
        } else {
            var drawableRes =
                if (isOnSeaker) R.drawable.audio_route_speaker else R.drawable.audio_route_earpiece
            binding.ibSpeakerVideo.setBackgroundResource(drawableRes)
            binding.speaker.setBackgroundResource(drawableRes)
//            binding.ivSpeaker.setImageResource(drawableRes)
        }
    }

    private fun toggleMicrophone(type: Module_Phone_FunctionKey) {
//        manager.setMicEnabled(!isMute)
        try {
            if (isMute) {
//                rinoManager.startPushAudioToChannel(agoraId)
                RinoAudioUtils.setMicrophoneMute(this, false)
            } else {
//                rinoManager.stopPushAudioToChannel(agoraId)
                RinoAudioUtils.setMicrophoneMute(this, true)
            }

            type.drawableResLandscape =
                if (isMute) R.drawable.icon_mic_normal_landscape else R.drawable.icon_mic_mute_landscape
            type.drawableRes = if (isMute) R.drawable.icon_mic_normal else R.drawable.icon_mic_mute

            isMute = !isMute
        } catch (e: Exception) {
            showToast(e.toString())
        }
    }

    private fun moveDown() {
        objectAnimator.run {
            objectAnimator =
                ObjectAnimator.ofFloat(binding.bottomControllerLayout, "translationY", toYData)

        }
        objectAnimator?.duration = duration
        objectAnimator?.addListener(object : Animator.AnimatorListener {

            override fun onAnimationStart(p0: Animator) {
            }

            override fun onAnimationEnd(p0: Animator) {
                binding.ivFold.setImageResource(R.drawable.expand_function_key)
            }

            override fun onAnimationCancel(p0: Animator) {
            }

            override fun onAnimationRepeat(p0: Animator) {
            }
        })
        objectAnimator?.start()
    }

    private fun moveUp() {
        objectAnimator.let {
            objectAnimator =
                ObjectAnimator.ofFloat(binding.bottomControllerLayout, "translationY", 0f)
        }
        objectAnimator?.duration = duration
        objectAnimator?.addListener(object : Animator.AnimatorListener {
            override fun onAnimationStart(p0: Animator) {
            }

            override fun onAnimationEnd(p0: Animator) {
                binding.ivFold.setImageResource(R.drawable.collapse_function_key)
            }

            override fun onAnimationCancel(p0: Animator) {
            }

            override fun onAnimationRepeat(p0: Animator) {
            }
        })
        objectAnimator?.start()
    }

    override fun onNewIntent(intent: Intent?) {
        super.onNewIntent(intent)
        Log.e("[CallActivity] onNewIntent")
        val action = intent?.action
        if (action.equals("endCall")) {
            finish()
        }
    }

    private val onClickListener = View.OnClickListener { view ->
        when (view.id) {
            R.id.call_fullscreen_layout -> {
//                safeHangup()
                NLog.e("========full_screen=========")
                MainScope().launch {
                    delay(800)
                    toggleFullScreen()
                }
            }

            R.id.remote_video_surface -> {
                if (isFullScreen) {
                    if (binding.controlLayout.visibility == View.VISIBLE) {
                        binding.controlLayout.visibility = View.GONE
                        binding.callTitleBar.root.visibility = View.GONE
                    } else {
                        binding.controlLayout.visibility = View.VISIBLE
                        binding.callTitleBar.root.visibility = View.VISIBLE
                    }
                }

                showMomentarily()
            }

            R.id.iv_fold -> {
                if (isMovedDown) {
                    moveUp()
                } else {
                    moveDown()
                }
                isMovedDown = !isMovedDown
            }

            R.id.hang_up -> safeHangup()
            R.id.ll_micphone -> {

                isMicEnable = !isMicEnable
//                manager.setMicEnabled(isMicEnable)

                val result = if (isMicEnable) {
//                    rinoManager.startPushAudioToChannel(agoraId)
                    RinoAudioUtils.setMicrophoneMute(this, false)
                } else {
//                    rinoManager.stopPushAudioToChannel(agoraId)
                    RinoAudioUtils.setMicrophoneMute(this, true)
                }
                if (result < 0) {
                    showToast(getString(R.string.switch_mic_error_tips))
                } else {
                    binding.micphone.isSelected = !isMicEnable
                }
            }

            R.id.ll_speaker -> {
//                isSpeakerEnable = !isSpeakerEnable
//                if (isSpeakerEnable) {
//                    AudioRouteUtils.routeAudioToSpeaker()
//                } else {
//                    AudioRouteUtils.routeAudioToEarpiece()
//                }
//                binding.speaker.isSelected = !isSpeakerEnable
                viewModel.showRouteDialog.value = true
                showAudioRouteSelectPopup()
            }

            R.id.iv_titlebar_back -> {
                safeHangup()
                onBackPressed()
            }

            R.id.bottom_controller_open_door -> {
                val remoteSipAccount = Module_Phone_VoipController.get().getRemoteSipAccount()
                val currentDevice =
                    getDeviceDataByUsername(remoteSipAccount)
                currentDevice?.doors?.let {
                    if (it.size == 1) {
                        var doorNum = currentDevice.doors?.get(0)?.doorNo ?: 1
                        openDoor(currentDevice.mac, doorNum)
                    } else {
                        showBottomSheetDialog(currentDevice.mac, it)
                    }
                }
            }
        }
        showMomentarily()
    }

    private fun safeHangup() {
        showLoading()
        if (joinChannelSuccess) {
            rinoManager.leaveChannel()
        } else {
            val hangUp = manager.hangUp()
            //意外挂掉失败,结束当前Activity
            if (!hangUp) finish()
        }
    }

    private fun showMomentarily() {
        if (isFullScreen) {
            stopTimer()
//            startTimer()
        } else {
            stopTimer()
            binding.controlLayout.visibility = View.VISIBLE
            binding.callTitleBar.root.visibility = View.VISIBLE
        }
    }

    private fun startTimer() {
        timer?.cancel()
        timer = Timer("Hide UI controls scheduler")
        timer?.schedule(object : TimerTask() {
            override fun run() {
                sHandler.post {
                    binding.controlLayout.visibility = View.GONE
                    binding.callTitleBar.root.visibility = View.GONE
                }
            }

        }, 8000)
    }

    private fun stopTimer() {
        timer?.cancel()
    }

    private fun muteDevice(isMute: Boolean) {
        when (isMute) {
            true -> {
                manager.setMicEnabled(false)
                Module_Phone_AudioRouteUtils.routeAudioToEarpiece()
            }

            false -> {
                manager.setMicEnabled(true)
                Module_Phone_AudioRouteUtils.routeAudioToSpeaker()
            }
        }
    }

    private fun showBottomSheetDialog(mac: String, data: MutableList<DoorData>) {
        val bottomSheetDialog = BottomSheetDialog(this, R.style.BottomSheetDialog)
        bottomSheetDialog.setCanceledOnTouchOutside(true)


        val view = LayoutInflater.from(this).inflate(R.layout.bottom_dialog, null, false)

        view.findViewById<TextView>(R.id.center_title).setText(R.string.door_lock_select)
        val recyclerView = view.findViewById<RecyclerView>(R.id.recyclerView)
        view.findViewById<ImageView>(R.id.image_cancel).setOnClickListener {
            bottomSheetDialog.dismiss()
        }

        val dtmfAdapter = Module_Phone_DtmfAdapter(data)
        recyclerView.adapter = dtmfAdapter
        recyclerView.addItemDecoration(SpaceItemDecoration(this))

        dtmfAdapter.setOnItemChildClickListener { _, _, position ->
            openDoor(mac, data[position].doorNo)
            bottomSheetDialog.dismiss()
        }

        bottomSheetDialog.setContentView(view)
        bottomSheetDialog.show()
    }

    private fun openDoor(mac: String, doorNo: Int) {
        showLoading()
        MqttDoorOperator.sendOpenDoorAction(
            userId,
            mac,
            doorNo,
            1
        )
//        KcpTaskHelper.instance.requestOpenDoor(OpenDoorRequest(accessToken,cUserId, KcpRquestType.TYPE_ACTION_OPEN_DOOR,
//            OpenDoorBody(mac = mac, doors = arrayListOf(OpenDoorItem(doorNo,1)))))
    }

    @Subscribe(threadMode = ThreadMode.MAIN)
    fun onEventMsgReceived(event: MessageEvent) {
        when (event.msgType) {
            TYPE_APP_OPEN_DOOR_RESULT -> {
                val event = event.msgContent as OpenDoorResultBean
                showOpenDoorResultDialog(event)
            }

            TYPE_CALL_SEND_CREATE_CHANNEL_RESULT_INCALL -> {
                rinoManager.joinChannel(
                    binding.remoteVideoSurface,
                    agoraId,
                    isSpeakerOn = viewModel.curCallIsVideoEnable.value == true
                )
            }

            TYPE_CALL_UPDATE_TOKEN_RESULT -> {
                NLog.e("[CallActivity]....TYPE_CALL_UPDATE_TOKEN_RESULT")
                rinoManager.updateRinoToken()
            }

            TYPE_CALL_UPDATE_CHANNEL_RESULT -> {
                NLog.e("[CallActivity]....TYPE_CALL_UPDATE_CHANNEL_RESULT")
                rinoManager.joinChannel(
                    binding.remoteVideoSurface,
                    agoraId,
                    isSpeakerOn = viewModel.curCallIsVideoEnable.value == true
                )
            }

            TYPE_CALL_SEND_CREATE_CHANNEL_RESULT_TRYING -> {
                NLog.e("[CallActivity]....TYPE_CALL_SEND_CREATE_CHANNEL_RESULT_TRYING")
                rinoManager.joinChannel(
                    binding.remoteVideoSurface,
                    agoraId,
                    isSpeakerOn = viewModel.curCallIsVideoEnable.value == true
                )
            }

            TYPE_CALL_SEND_CREATE_CHANNEL_RESULT_INCOMING -> {
                NLog.e("[CallActivity]....TYPE_CALL_SEND_CREATE_CHANNEL_RESULT_INCOMING")
                rinoManager.joinChannel(
                    binding.remoteVideoSurface,
                    agoraId,
                    isSpeakerOn = viewModel.curCallIsVideoEnable.value == true
                )
            }

            TYPE_CALL_SEND_CREATE_CHANNEL_RESULT_INCOMING_EARLYMEDIA -> {
                NLog.e("[CallActivity]....TYPE_CALL_SEND_CREATE_CHANNEL_RESULT_INCOMING_EARLYMEDIA")
                rinoManager.joinChannel(
                    binding.remoteVideoSurface,
                    agoraId,
                    isSpeakerOn = viewModel.curCallIsVideoEnable.value == true
                )
            }

            TYPE_APP_BLE_OPEN_DOOR_RESULT -> {
                val show = event.msgContent as String
                val event = OpenDoorResultBean(
                    msgId = "",
                    which = 0,
                    success = show.equals("1"),
                    reason = ""
                )
                showOpenDoorResultDialog(event, true)
            }

            TYPE_APP_SHOW_WAITING_DIALOG -> {
                val show = event.msgContent as Int
                NLog.e("TYPE_APP_SHOW_WAITING_DIALOG : $show")
                if (show == 1) {
                    showWaitingDialog(getString(com.newlink.building.common_base.R.string.dialog_content_ble_open))
                } else {
                    hideWaitingDialog()
                }
            }
        }
    }

    private var mWaitProgressDialog: ProgressDialog? = null
    private fun showWaitingDialog(msg: String) {
        if (mWaitProgressDialog?.isShowing == true) {
            return
        }
        mWaitProgressDialog = DialogUtil.getWaitDialog(this, msg)
        if (mWaitProgressDialog != null) {
            NLog.e(">>>> showWaitingDialog")
            mWaitProgressDialog?.show()
        } else {
            NLog.e(">>>> showWaitingDialog failed....")
        }
    }

    private fun hideWaitingDialog() {
        if (mWaitProgressDialog?.isShowing == true) {
            NLog.e("<<<<< hideWaitingDialog")
            mWaitProgressDialog?.dismiss()
            mWaitProgressDialog = null
        }
    }


    @Subscribe(threadMode = ThreadMode.MAIN)
    public fun onOpenDoorResultKcp(event: OpenDoorKcpActionEvent) {
//        showOpenDoorResultDialog(event)
    }

    private fun showOpenDoorResultDialog(event: OpenDoorResultBean, isBle: Boolean = false) {
        NLog.e("[jake] .. CallActivity :  onOpenDoorResultKcp : ${event.success}")
        hideLoading()
        if (event != null) {
            dismissDialog()
            val openDoorResult = event.success
            val openDoorReason = event.reason

            val successStrVal =
                resources.getString(com.newlink.building.common_base.R.string.open_door_success_str)
            val failedStrVal = if (isBle) {
                resources.getString(com.newlink.building.common_base.R.string.open_door_failed_str)
            } else {
                if (openDoorResult) successStrVal else {
                    if (openDoorReason == "-1") {
                        resources.getString(com.newlink.building.common_base.R.string.result_offline)
                    } else {
                        resources.getString(com.newlink.building.common_base.R.string.open_door_failed_str)
                    }
                }
            }
            val resultVal = if (openDoorResult) successStrVal else failedStrVal
            val openDoorResultStr = resultVal
            val drawable = if (openDoorResult) {
                com.newlink.building.common_base.R.drawable.toast_success_green
            } else {
                if (isBle) {
                    com.newlink.building.common_base.R.drawable.toast_failed_red
                } else {
                    com.newlink.building.common_base.R.drawable.toast_warning_yellow
                }
            }
            openDoorDialog = ToastSettingDialog.Builder(this).tittle("").drawable(drawable)
                .message(openDoorResultStr).build()
            openDoorDialog?.show()
        }
    }

    private fun releaseDialog() {
        if (openDoorDialog != null) {
            openDoorDialog?.clearTask()
        }
    }

    private fun dismissDialog() {
        if (openDoorDialog != null && openDoorDialog?.isShowing == true) {
            openDoorDialog?.dismiss()
        }
    }

    private var mAudioManager: AudioManager? = null

    override fun dispatchKeyEvent(event: KeyEvent): Boolean {
        if (mAudioManager == null) {
            mAudioManager = getSystemService(Service.AUDIO_SERVICE) as AudioManager
        }

        if (event.keyCode == KeyEvent.KEYCODE_VOLUME_DOWN) {
            if (Module_Phone_Compatibility.volumeDown(this@Module_Phone_CallActivity, mAudioManager!!)) {
                val v = mAudioManager?.getStreamVolume(AudioManager.STREAM_MUSIC)
                Log.e("Compatibility.. volume down ::: $v")
                return true
            }
        } else if (event.keyCode == KeyEvent.KEYCODE_VOLUME_UP) {
            if (Module_Phone_Compatibility.volumeUp(this@Module_Phone_CallActivity, mAudioManager!!)) {
                val v = mAudioManager?.getStreamVolume(AudioManager.STREAM_MUSIC)
                Log.e("Compatibility.. volume up ::: $v")
                return true
            }
        }
        return super.dispatchKeyEvent(event)

    }

    private fun endPip() {

        if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
            if (isInPictureInPictureMode) {
                val pipBuilder = PictureInPictureParams.Builder()
                pipBuilder.setAspectRatio(Rational(16, 9))
                enterPictureInPictureMode(pipBuilder.build())
            }
        }
    }

    //通过call state获取当前的通话
    fun findCurrentCall(): Call? {
        for (call in coreContext.core.calls) {
            Log.e("[Call Activity]  state:${call.state}")
            if (call.state == Call.State.Connected ||
                call.state == Call.State.StreamsRunning ||
                call.state == Call.State.Paused ||
                call.state == Call.State.Pausing ||
                call.state == Call.State.Resuming ||
                call.state == Call.State.Updating ||
                call.state == Call.State.PausedByRemote
            ) {
                if (call.state == Call.State.Paused || call.state == Call.State.Pausing) {
                    //在MainActivity中由于requestAudioFocus会导致通话处于Pausing,此时需要恢复通话
                    call.resume()
                }
                return call
            }
        }
        return null
    }

    private val mDialog by lazy {
        DialogUtil.getWaitDialog(this)
    }

    private fun showLoading() {
        mDialog.show()
    }

    private fun hideLoading() {
        mDialog.dismiss()
    }
}
